/***
 * @file GenExp.cpp
 *
 *  @Author: calle
 *  @date May 1, 2011
 *  
 *  @brief
 *  	
 */

#include <Generator/GenExp.h>
#include <AbsSyn/BoolExp.h>
#include <AbsSyn/IntExp.h>
#include <AbsSyn/DoubleExp.h>
#include <Interfaces/IExp.h>
#include <AbsSyn/IdentExp.h>
#include <Generator/context/IContext.h>
#include <cstdlib>
#include <AbsSyn/IntExp.h>
#include <Helpers/SlumpGenerator.h>
#include <assert.h>
#include <Generator/Exp/GenConstant.h>
#include <AbsSyn/IdentExp.h>
#include <Generator/Exp/GenNonTerminal.h>
#include <AbsSyn/TypeEnum.h>



GenExp::GenExp(const ExpRegistry& registry,TypeEnum type,IContext* const currentContext, unsigned int depth):
m_Registry(registry),
m_Type(type),
m_currentContext(currentContext),
m_Depth(depth)

{



}

GenExp::~GenExp() {

}



AbsSyn::IExpPtr GenExp::genExp(MODE mode)
{
	AbsSyn::IExpPtr ret;

	switch(mode)
	{
	case RANDOM:
		m_Mode = TerminalState::getRandom();
		break;
	case TERMINAL:
		m_Mode = TerminalState::TERMINAL;
		break;
	case NON_TERMINAL:
		//m_Mode = TerminalState::NON_TERMINAL;
		m_Mode = ( m_Depth != 0 ? TerminalState::NON_TERMINAL : TerminalState::TERMINAL);
		break;
	default:
		break;
	}

	std::cout << "Depth: " << m_Depth << " mode:"<< m_Mode.toString() << std::endl;
	//We have choosen a expression, we now need to construct it.
	(m_Depth > 0 ? m_Depth--: m_Depth = 0);
	if( m_Mode == TerminalState::TERMINAL)
	{
		ret = handleTerminal();
	}else if( m_Mode == TerminalState::NON_TERMINAL )
	{
		ret = handleNonTerminal();
	}


	return ret;
}





AbsSyn::IExpPtr GenExp::handleNonTerminal() const
{
	AbsSyn::IExpPtr ret;
	/**
	 * Non terminals are those that are dependent on other
	 * expressions for example add, subtract, comparison (==)
	 * To be able to create one we need first to know
	 * what kinds there are.
	 * This can be done from the ExpRegistry.
	 * i.e m_Registry.getAll(
	 *
	 *
	 */

	//IContext::IContextPtr contextPtr(m_currentContext);
	GenNonTerminal generator(m_Registry,m_Type,m_currentContext,m_Depth);

	ret =  generator.genNonTerminal();

	assert(ret);

	return ret;

}



AbsSyn::IExpPtr GenExp::handleTerminal() const
{
	AbsSyn::IExpPtr ret;

	Terminals mode = Terminals::getRandomTerminal();


	if( mode == Terminals::CONSTANT)
	{
		ret = genConstant();
	}else if ( mode == Terminals::IDENT)
	{
		/*
		 * need to check if there exists any ident in context.
		 * a list of idents from the context with that specific type
		 * choose randomly that indent. If no ident exists
		 * then we can't use ident so we shouldt be here!
		 * Check before
		 */


		ret = handleIdent();
	}


	assert(ret);
	std::cout << "Ending terminals" << std::endl;
	return ret;
}




AbsSyn::IExpPtr GenExp::genConstant() const
{
	GenConstant constantGen(m_Type,m_Registry);

	AbsSyn::IExpPtr ptr = constantGen.genConstant();
	assert(ptr);
	return ptr;//constant.genConstant();
}






AbsSyn::IExpPtr GenExp::handleIdent() const
{
	AbsSyn::IExpPtr ret;
	IContext::TypeList list;


	m_currentContext->getAllOfType(m_Type,list);

	//Choose a random ident from the context of the type ,
	// if it doens't exists any of that kind we need to
	// create a constant and return.
	if( !list.empty() )
	{

		int nr = static_cast<int>(rand()%(list.size()));
		Ident id = list.at(nr);

		ret.reset(new IdentExp(&id));
		std::cout << "Choosen ident " << ret->toString() << std::endl;
	}else
	{
		ret = genConstant();
	}





	return ret;
}









